/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.list.boost;

import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetValueRequestMessage;
import cn.easyplatform.messages.request.ListPagingRequestMessage;
import cn.easyplatform.messages.request.ListReloadRequestMessage;
import cn.easyplatform.messages.response.ListQueryResponseMessage;
import cn.easyplatform.messages.vos.GetValueVo;
import cn.easyplatform.messages.vos.datalist.*;
import cn.easyplatform.spi.service.ListService;
import cn.easyplatform.spi.service.TaskService;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.ext.zul.Datalist;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.support.ExpressionEngine;
import cn.easyplatform.web.task.support.ManagedComponent;
import cn.easyplatform.web.task.support.ManagedComponents;
import cn.easyplatform.web.task.support.SupportFactory;
import cn.easyplatform.web.task.zkex.list.AbstractListBuilder;
import cn.easyplatform.web.task.zkex.list.exporter.ExportBean;
import cn.easyplatform.web.utils.ExtUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Components;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SortEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;
import org.zkoss.zul.event.PagingEvent;
import org.zkoss.zul.event.ZulEvents;
import org.zkoss.zul.impl.MeshElement;

import java.util.*;

/**
 * @Author: <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @Description:
 * @Since: 2.0.0 <br/>
 * @Date: Created in 2019/7/30 15:27
 * @Modified By:
 */
public abstract class AbstractBoostBuilder extends AbstractListBuilder implements ManagedComponent, ManagedComponents, EventListener<Event> {

    protected Grid listExt;

    protected ExpressionEngine expressionEngine;

    private ExpressionEngine footerEngine;

    protected Paging paging;

    protected AbstractBoostBuilder(OperableHandler mainTaskHandler, Datalist entity, Component anchor) {
        super(mainTaskHandler, entity, anchor);
        listExt = new Grid();
        listExt.setSpan(entity.isSpan());
        listExt.setSizedByContent(entity.isSizedByContent());
        listExt.setSclass(entity.getSclass());
        listExt.setClass(entity.getSclass());
        listExt.setSclass(entity.getSclass());
        listExt.setEvent(entity.getEvent());
        listExt.setHeight(entity.getHeight());
        listExt.setWidth(entity.getWidth());
        listExt.setId(entity.getId());
        listExt.setHflex(entity.getHflex());
        listExt.setVflex(entity.getVflex());
        listExt.setVisible(entity.isVisible());
        listExt.setOddRowSclass(entity.getOddRowSclass());
        listExt.setStyle(entity.getStyle());
        listExt.setTooltiptext(entity.getTooltiptext());
        listExt.setTooltip(entity.getTooltip());
        listExt.setTop(entity.getTop());
        listExt.setLeft(entity.getLeft());
        listExt.setMold(entity.getMold());
        listExt.setEmptyMessage(entity.getEmptyMessage());
        //if (Contexts.getEnv().getDeviceType().equals(DeviceType.MOBILE.getName()))
        //    listExt.setWidgetAttribute("data-swipeable", "true");
    }


    @Override
    protected Component createContent() {
        Borderlayout layout = new Borderlayout();
        if (getEntity().isShowPanel() && !Strings.isBlank(this.layout.getPanel())) {
            North north = new North();
            north.setHflex("1");
            north.setVflex("1");
            north.setBorder("none");
            if (this.layout instanceof ListRecurVo)
                north.appendChild(buildPanel(this.layout.getPanel(),
                        ((ListRecurVo) this.layout).getPanelQueryInfo()));
            else
                north.appendChild(buildPanel(this.layout.getPanel(), null));
            layout.appendChild(north);
        }
        Center center = new Center();
        center.setHflex("1");
        center.setVflex("1");
        center.setBorder("none");
        layout.appendChild(center);
        if (!Strings.isBlank(listExt.getHeight()))
            layout.setHeight(listExt.getHeight());
        else if (!Strings.isBlank(listExt.getVflex()))
            layout.setVflex(listExt.getVflex());
        else
            layout.setVflex("1");
        if (!Strings.isBlank(listExt.getWidth()))
            layout.setWidth(listExt.getWidth());
        else if (!Strings.isBlank(listExt.getHflex()))
            layout.setHflex(listExt.getHflex());
        else
            layout.setHflex("1");
        if (Strings.isBlank(listExt.getWidth()))
            listExt.setHflex("1");
        if (Strings.isBlank(listExt.getHeight()))
            listExt.setVflex("1");
        Components.replace(getEntity(), layout);
        listExt.setParent(center);
        return layout;
    }

    @Override
    protected void createHeader() {
        ExpressionEngine headerEngine = null;
        Map<String, Component> managedComponents = null;
        if (!Strings.isBlank(layout.getOnHeader())) {
            headerEngine = SupportFactory.getExpressionEngine(this,
                    layout.getOnHeader());
            managedComponents = new HashMap<String, Component>();
        }
        Columns head = null;
        if (listExt.getColumns() == null) {
            head = new Columns();
            head.setStyle(headStyle);
            head.setParent(listExt);
        } else {
            head = listExt.getColumns();
            head.getChildren().clear();
        }
        head.setSizable(getEntity().isSizable());
        List<String> invisibleColumns = null;
        if (!Strings.isBlank(getEntity().getInvisibleColumns()))
            invisibleColumns = Lang.array2list(getEntity()
                    .getInvisibleColumns().split("\\,"));
        List<String> editableFields = null;
        if (!Strings.isBlank(getEntity().getEditableColumns())) {
            editableFields = new ArrayList<String>();
            String[] fields = getEntity().getEditableColumns().split("\\,");
            for (String f : fields) {
                f = f.trim();
                if (f.equals(""))
                    continue;
                editableFields.add(f);
            }
            if (editableFields.isEmpty())
                editableFields = null;
        }
        int size = layout.getHeaders().size();
        for (int i = 0; i < size; i++) {
            ListHeaderVo hv = layout.getHeaders().get(i);
            boolean isVisible = invisibleColumns == null
                    || !invisibleColumns.contains(hv.getName());
            Column header = new Column();
            header.setValue(hv);
            boolean isEditable = false;
            if (!Strings.isBlank(hv.getName()))
                isEditable = editableFields == null ? false : editableFields.contains(hv.getName()) || editableFields.get(0).equals("*");
            hv.setEditable(isEditable);
            if (isVisible && hv.isVisible()) {
                if (getEntity().isShowTitle()) {
                    if (hv.getTitle() != null && hv.getTitle().startsWith("$")) {
                        useHeaderVariable = true;
                        IResponseMessage<?> resp = ServiceLocator
                                .lookup(TaskService.class).getValue(
                                        new GetValueRequestMessage(getId(),
                                                new GetValueVo(hv.getTitle()
                                                        .substring(1))));
                        if (!resp.isSuccess()) {
                            Clients.wrongValue(listExt, (String) resp.getBody());
                            return;
                        }
                        header.setLabel((String) resp.getBody());
                    } else
                        header.setLabel(hv.getTitle());
                }
                if (!listExt.isSizedByContent()) {
                    if (NumberUtils.isDigits(hv.getWidth()))
                        header.setWidth(hv.getWidth() + "px");
                    else
                        header.setWidth(hv.getWidth());
                }
                if (!Strings.isBlank(hv.getAlign()))
                    header.setAlign(hv.getAlign());
                else if (hv.getType() == FieldType.INT
                        || hv.getType() == FieldType.LONG
                        || hv.getType() == FieldType.NUMERIC)
                    header.setAlign("right");
                if (!Strings.isBlank(hv.getValign()))
                    header.setValign(hv.getValign());
                if (!Strings.isBlank(hv.getStyle()))
                    header.setStyle(hv.getStyle());
                if (!Strings.isBlank(hv.getIconSclass()))
                    header.setIconSclass(hv.getIconSclass());
                if (!Strings.isBlank(hv.getHoverimg()))
                    header.setHoverImage(hv.getHoverimg());
                if (!Strings.isBlank(hv.getImage()))
                    header.setImage(hv.getImage());
                if (!Strings.isBlank(hv.getStyle()))
                    header.setStyle(hv.getStyle());
                if (!hasTotal)
                    hasTotal = hv.isTotal();
                if (hv.isTotal())
                    buildGroup(hv, i);
                if (hv.isSort()) {
                    Comparator<Listitem> comparator = ExtUtils
                            .createEmptyComparator();
                    header.setSortAscending(comparator);
                    header.setSortDescending(comparator);
                    header.addEventListener(Events.ON_SORT, this);
                    header.setTooltiptext(Labels.getLabel("datalist.header.order"));
                } else
                    header.setTooltiptext(hv.getName());
            } else {
                hv.setVisible(false);
                header.setVisible(false);
                //header.setStubonly(true);
                header.setLabel(hv.getTitle());
            }
            if (managedComponents != null)
                managedComponents.put(hv.getName(), header);
            head.appendChild(header);
        }
        head.setVisible(getEntity().isShowTitle());
        if (getEntity().getPopup() == null
                || getEntity().getPopup().equalsIgnoreCase("true"))
            setMenupopup(head);
        if (getEntity().getFrozenColumns() > 0) {
            Frozen frozen = null;
            if (listExt.getFrozen() == null) {
                frozen = new Frozen();
                listExt.appendChild(frozen);
            } else
                frozen = listExt.getFrozen();
            frozen.setColumns(getEntity().getFrozenColumns());
            frozen.setStyle(getEntity().getFrozenStyle());
            frozen.setRightColumns(getEntity().getFrozenRightColumns());
            frozen.setStart(getEntity().getFrozenStart());
        }
        invisibleColumns = null;
        if (managedComponents != null) {
            managedComponents.put("self", head);
            headerEngine.exec(null, managedComponents, null);
            headerEngine = null;
            managedComponents = null;
        }
        if (hasTotal) {
            if (!Strings.isBlank(layout.getOnFooter()))
                footerEngine = SupportFactory.getExpressionEngine(this,
                        layout.getOnFooter());
            Foot foot = null;
            if (listExt.getFoot() == null) {
                foot = new Foot();
                foot.setStyle(footStyle);
                listExt.appendChild(foot);
            } else {
                foot = listExt.getFoot();
                foot.getChildren().clear();
            }
            for (ListHeaderVo hv : layout.getHeaders()) {
                Footer footer = new Footer();
                if (hv.isTotal()) {
                    footer.setStyle(hv.getTotalStyle());
                    if (!Strings.isBlank(hv.getAlign()))
                        footer.setAlign(hv.getAlign());
                    else if (hv.getType() == FieldType.INT
                            || hv.getType() == FieldType.LONG
                            || hv.getType() == FieldType.NUMERIC)
                        footer.setAlign("right");
                }
                foot.appendChild(footer);
            }
        }
    }

    protected void buildGroup(ListHeaderVo hv, int index) {
    }

    /**
     * 重新加载
     */
    public void reload(String... comps) {
        ListService dls = ServiceLocator
                .lookup(ListService.class);
        IResponseMessage<?> resp = dls.doReload(new ListReloadRequestMessage(
                mainTaskHandler.getId(), new ListReloadVo(listExt.getId(),
                null, false)));
        if (resp.isSuccess()) {
            if (useHeaderVariable)
                createHeader();
            ListQueryResultVo rv = ((ListQueryResponseMessage) resp).getBody();
            clear();
            listIndex = 0;
            if (rv.getErrMsg() != null)
                setEmptyMessage(rv.getErrMsg());
            else {
                redraw(rv.getData());
                refreshFoot();
            }
        } else
            throw new BackendException(resp);
    }

    @Override
    public void refreshFoot() {
        if (!hasTotal)
            return;
        Foot foot = listExt.getFoot();
        Map<String, Component> managedComponents = null;
        Map<String, Object> evalMap = null;
        if (footerEngine != null) {
            managedComponents = new HashMap<String, Component>();
            evalMap = new HashMap<String, Object>();
            managedComponents.put("self", foot);
        }
        for (int i = 0; i < layout.getHeaders().size(); i++) {
            ListHeaderVo hv = layout.getHeaders().get(i);
            if (hv.isTotal()) {
                Footer footer = (Footer) foot.getChildren().get(i);
                if (Strings.isBlank(hv.getTotalName()))
                    footer.setLabel(hv.getTitle() + ":" + hv.getValue());
                else
                    footer.setLabel(hv.getTotalName() + ":" + hv.getValue());
                if (footerEngine != null) {
                    evalMap.put("SYS_COUNT", hv.getCount());
                    evalMap.put("SYS_TOTAL", hv.getTotal());
                    evalMap.put(hv.getName(), hv.getRawValue());
                }
            }
            if (footerEngine != null) {
                managedComponents.put(hv.getName(), foot.getChildren().get(i));
                if (!hv.isTotal())
                    evalMap.put(hv.getName(), 0);
            }
        }
        if (footerEngine != null)
            footerEngine.exec(null, managedComponents, evalMap);
    }

    @Override
    public void onEvent(Event event) throws Exception {
        if (event.getName().equals(Events.ON_SORT)) {
            SortEvent evt = (SortEvent) event;
            doSort((Column) evt.getTarget(), evt.isAscending());
        } else if (Lang.equals(event.getTarget().getEvent(), "query()")) {
            query(null);
        } else if (event.getName().equals(ZulEvents.ON_PAGING)) {
            PagingEvent evt = (PagingEvent) event;
            paging(evt.getActivePage() + 1);
        } else if (event.getName().equals("onLater")) {//导出提示
            Session session = event.getTarget().getDesktop().getSession();
            int maxInactiveInterval = session.getMaxInactiveInterval();
            //如果数据量大，防止会话过期
            session.setMaxInactiveInterval(-1);
            ExportBean eb = (ExportBean) event.getData();
            Grid backup = listExt;
            try {
                listExt = (Grid) listExt.clone();
                listExt.detach();
                clear();
                //每500笔一次导出，减少服务端内存
                final int PZ = 500;
                int count = paging.getTotalSize() / PZ;
                if (paging.getTotalSize() % PZ > 0) {
                    count++;
                }
                for (int i = 1; i <= count; i++) {
                    ListService ds = ServiceLocator.lookup(ListService.class);
                    IResponseMessage resp = ds.doPaging(new ListPagingRequestMessage(getId(), new ListPagingVo(getEntity().getId(), i, PZ)));
                    if (resp.isSuccess()) {
                        redraw((List<ListRowVo>) resp.getBody());
                    } else {
                        MessageBox.showMessage(resp);
                        return;
                    }
                }
                export(listExt, eb.getType(), eb.getExportHeaders());
            } finally {
                listExt = null;
                listExt = backup;
                session.setMaxInactiveInterval(maxInactiveInterval);
                listExt.removeEventListener("onLater", this);
                Clients.clearBusy();
            }
        }
    }

    @Override
    protected void addEventListener(Component comp, String evtName) {
        if (evtName == null)
            comp.addEventListener(Events.ON_CLICK, this);
        else
            comp.addEventListener(evtName, this);
    }

    @Override
    public Map<String, Component> getManagedComponents() {
        Component row = (Component) listExt.getAttribute("selectRow");
        if (row == null) {
            Event evt = Contexts.getEvent();
            if (evt != null) {
                if (evt.getTarget() instanceof Row) {
                    Row sel = (Row) evt.getTarget();
                    if (sel.getGrid() == listExt)
                        return WebUtils.getManagedComponents(this, sel);
                }
                Component parent = evt.getTarget().getParent();
                if (parent != null) {
                    int deep = 0;
                    while (deep < 6) {
                        if (parent instanceof Row) {
                            Row sel = (Row) parent;
                            if (sel.getGrid() == listExt) {
                                return WebUtils.getManagedComponents(this, sel);
                            } else
                                return null;
                        } else {
                            parent = parent.getParent();
                            if (parent == null || parent instanceof Grid
                                    || parent instanceof Window)
                                break;
                        }
                        deep++;
                    }
                }
            }
            return Collections.EMPTY_MAP;
        } else
            return WebUtils.getManagedComponents(this, row);
    }

    @Override
    public void exportAll(String type, Object... exportHeaders) {
        if (getEntity().getType().equals(Constants.REPORT) || getEntity().isFetchAll() || getEntity().getPageSize() == 0) {
            export(listExt, type, exportHeaders);
        } else {
            Clients.showBusy(Labels.getLabel("message.long.operation"));
            listExt.addEventListener("onLater", this);
            Events.echoEvent("onLater", listExt, new ExportBean(type, exportHeaders));
        }
    }

    @Override
    public MeshElement getComponent() {
        return listExt;
    }

    @Override
    public void paging(int pageNo) {
    }

    @Override
    public void clear() {
        listExt.getRows().getChildren().clear();
        for (ListHeaderVo header : layout.getHeaders()) {
            if (header.isTotal())
                header.reset();
        }
        listIndex = 0;
    }

    @Override
    public ListRowVo getSelectedValue() {
        Row row = (Row) listExt.getAttribute("selectRow");
        if (row == null)
            return null;
        return row.getValue();
    }

    @Override
    protected void setEmptyMessage(String msg) {
        listExt.setEmptyMessage(msg);
    }

    @Override
    protected void setPagingInfo(int totalSize, int activePageNo) {
    }

    @Override
    public void setPageSize(int pageSize) {
    }

    @Override
    public boolean validate(Component c) {
        return false;
    }

    @Override
    public String getId() {
        return mainTaskHandler.getId();
    }

    @Override
    public OperableHandler getParent() {
        return mainTaskHandler;
    }
}
